Warning: This document is for the development version of AutoGIS.

This page was generated from source/notebooks/L5/interactive-map-folium.ipynb.
Binder badge
Binder badge CSC badge

Interactive maps on Leaflet

Whenever you go into a website that has some kind of interactive map, it is quite probable that you are wittnessing a map that has been made with a JavaScipt library called Leaflet (the other popular one that you might have wittnessed is called OpenLayers.

There is also a Python module called Folium that makes it possible visualize data that’s been manipulated in Python on an interactive Leaflet map.

Creating a simple interactive web-map

Let’s first see how we can do a simple interactive web-map without any data on it. We just visualize OpenStreetMap on a specific location of the a world.

  • First thing that we need to do is to create a Map instance. There are few parameters that we can use to adjust how in our Map instance that will affect how the background map will look like.
  • See documentation of class folium.folium.Map() for all avaiable options.
In [91]:
import folium

# Create a Map instance
m = folium.Map(location=[60.25, 24.8],
    zoom_start=10, control_scale=True)

The first parameter location takes a pair of lat, lon values as list as an input which will determine where the map will be positioned when user opens up the map. zoom_start -parameter adjusts the default zoom-level for the map (the higher the number the closer the zoom is). control_scale defines if map should have a scalebar or not.

  • Let’s see what our map looks like:
In [92]:
m
Out[92]:
  • We can also save the map already now
  • Let’s save the map as a html file base_map.html:
In [93]:
outfp = "base_map.html"
m.save(outfp)

Navigate to the location where you saved the html file and open it in a web browser (preferably Google Chrome) to see the output.

  • Let’s change the basemap style to Stamen Toner and change the location of our map slightly. The tiles -parameter is used for changing the background map provider and map style (see the documentation for all possible ones).
In [94]:
# Let's change the basemap style to 'Stamen Toner'
m = folium.Map(location=[40.730610, -73.935242], tiles='Stamen Toner',
                zoom_start=12, control_scale=True, prefer_canvas=True)

m
Out[94]:
  • let’s also save this map as a html file:
In [95]:
# Filepath to the output
outfp = "base_map2.html"

# Save the map
m.save(outfp)

Task

Let’s take a few moments and play around with the parameters. Save the map and see how those changes affect the look of the map.

Adding layers to the map

Adding layers to a web-map is fairly straightforward and similar procedure as with Bokeh and we can use familiar tools to handle the data, i.e. Geopandas. Our ultimate aim is to create a plot like this where population in Helsinki and the address points are plotted on top of a web-map:

Let’s first practice by adding the address points onto the Helsinki basemap: - read input points using Geopandas:

In [96]:
import geopandas as gpd

# File path
points_fp = r"dataE5/addresses.shp"

# Read the data
points = gpd.read_file(points_fp)

#Check input data
points.head()
Out[96]:
address id geometry
0 Kampinkuja 1, 00100 Helsinki, Finland 1001 POINT (24.9301701 60.1683731)
1 Kaivokatu 8, 00101 Helsinki, Finland 1002 POINT (24.9418933 60.1698665)
2 Hermanstads strandsväg 1, 00580 Helsingfors, F... 1003 POINT (24.9774004 60.18735880000001)
3 Itäväylä, 00900 Helsinki, Finland 1004 POINT (25.0919641 60.21448089999999)
4 Tyynenmerenkatu 9, 00220 Helsinki, Finland 1005 POINT (24.9214846 60.1565781)
In [99]:
# Convert points to GeoJson
#points_gjson = folium.features.GeoJson(points.to_json())
points_gjson = folium.features.GeoJson(points)

Now we have our population data stored as GeoJSON format which basically contains the data as text in a similar way that it would be written in the .geojson -file.

  • add the points onto the Helsinki basemap
In [100]:
# Create a Map instance
m = folium.Map(location=[60.25, 24.8], tiles = 'cartodbpositron', zoom_start=11, control_scale=True)

# Add points to the map instance
points_gjson.add_to(m)

# Alternative syntax for adding points to the map instance
#m.add_child(points_gjson)

#Show map
m
Out[100]:

Heat map

In [101]:
# Get lat and lon coordinates
points['lon'] = points["geometry"].x
points['lat'] = points["geometry"].y

# Conver lat and lon to numpy array (old method: .as_matrix())
points_array = points[['lat', 'lon']].values
In [102]:
points.head()
Out[102]:
address id geometry lon lat
0 Kampinkuja 1, 00100 Helsinki, Finland 1001 POINT (24.9301701 60.1683731) 24.930170 60.168373
1 Kaivokatu 8, 00101 Helsinki, Finland 1002 POINT (24.9418933 60.1698665) 24.941893 60.169866
2 Hermanstads strandsväg 1, 00580 Helsingfors, F... 1003 POINT (24.9774004 60.18735880000001) 24.977400 60.187359
3 Itäväylä, 00900 Helsinki, Finland 1004 POINT (25.0919641 60.21448089999999) 25.091964 60.214481
4 Tyynenmerenkatu 9, 00220 Helsinki, Finland 1005 POINT (24.9214846 60.1565781) 24.921485 60.156578
In [103]:
from folium.plugins import HeatMap

# Create a Map instance
m = folium.Map(location=[60.25, 24.8], tiles = 'stamentoner', zoom_start=11, control_scale=True)

# Add heatmap to map instance
# Available parameters: HeatMap(data, name=None, min_opacity=0.5, max_zoom=18, max_val=1.0, radius=25, blur=15, gradient=None, overlay=True, control=True, show=True)
HeatMap(points_array).add_to(m)

# Alternative syntax:
#m.add_child(HeatMap(points_array, radius=15))

# Show map
m
Out[103]:

Choroplet map

Next, let’s check how we can overlay a population map on top of a basemap using folium’s choropleth method. This method is able to read the geometries and attributes directly from a geodataframe. This example is modified from the Folium quicksart.

  • First, read in the population grid:
In [104]:
# Filepaths
fp = "dataE5/Vaestotietoruudukko_2015.shp"

# Read Data
data = gpd.read_file(fp)

# Check the data
data.head()
Out[104]:
INDEX ASUKKAITA ASVALJYYS IKA0_9 IKA10_19 IKA20_29 IKA30_39 IKA40_49 IKA50_59 IKA60_69 IKA70_79 IKA_YLI80 geometry
0 688 8 31.0 99 99 99 99 99 99 99 99 99 POLYGON ((25472499.99532626 6689749.005069185,...
1 703 6 42.0 99 99 99 99 99 99 99 99 99 POLYGON ((25472499.99532626 6685998.998064222,...
2 710 8 44.0 99 99 99 99 99 99 99 99 99 POLYGON ((25472499.99532626 6684249.004130407,...
3 711 7 64.0 99 99 99 99 99 99 99 99 99 POLYGON ((25472499.99532626 6683999.004997005,...
4 715 19 23.0 99 99 99 99 99 99 99 99 99 POLYGON ((25472499.99532626 6682998.998461431,...
  • re-project layer into WGS 84 (epsg: 4326)
  • Modify columns:
In [105]:
# Re-project to WGS84
data = data.to_crs(epsg=4326)

# Check layer crs definition
print(data.crs)

# Make a selection (only data above 0 and below 1000)
data = data.loc[(data['ASUKKAITA'] > 0) & (data['ASUKKAITA'] <= 1000)]

# Create a Geo-id which is needed by the Folium (it needs to have a unique identifier for each row)
data['geoid'] = data.index.astype(str)

# Select only needed columns
data = data[['geoid', 'ASUKKAITA', 'geometry']]

# Convert to geojson (not needed for the simple coropleth map!)
#pop_json = data.to_json()

#check data
data.head()
{'init': 'epsg:4326', 'no_defs': True}
Out[105]:
geoid ASUKKAITA geometry
0 0 8 POLYGON ((24.50236241370834 60.31927864851716,...
1 1 6 POLYGON ((24.50287385337343 60.28562263749414,...
2 2 8 POLYGON ((24.50311210582754 60.26991652312412,...
3 3 7 POLYGON ((24.50314612020954 60.26767278939882,...
4 4 19 POLYGON ((24.50328212493893 60.25869775697638,...
In [106]:
# Create a Map instance
m = folium.Map(location=[60.25, 24.8], tiles = 'cartodbpositron', zoom_start=10, control_scale=True)

# Plot a choropleth map
# Notice: 'geoid' column that we created earlier needs to be assigned always as the first column
m.choropleth(
    geo_data=data,
    name='choropleth',
    data=data,
    columns=['geoid', 'ASUKKAITA'],
    key_on='feature.id',
    fill_color='YlOrRd',
    fill_opacity=0.7,
    line_opacity=0.2,
    line_color='white',
    line_weight=0,
    highlight=False,
    smooth_factor=1.0,
    #threshold_scale=[100, 250, 500, 1000, 2000],
    legend_name= 'Population in Helsinki')

#Show map
m
Out[106]:

Clustered point map

Let’s visualize the address points (locations of transport stations in Helsinki) on top of the choropleth map using clustered markers.

In [107]:
from folium.plugins import MarkerCluster

# Create a Clustered map where points are clustered
marker_cluster = MarkerCluster().add_to(m)
In [108]:
# Create clustered points on top of the map
for idx, row in points.iterrows():
    # Get lat and lon of points
    lon = row['geometry'].x
    lat = row['geometry'].y

    # Get address information
    address = row['address']
    # Add marker to the map
    folium.RegularPolygonMarker(location=[lat, lon], popup=address, fill_color='#2b8cbe', number_of_sides=6, radius=8).add_to(marker_cluster)

In [109]:
#Show map:
m
Out[109]:
In [ ]: